/**
 * SPDX-License-Identifier: GPL-2.0
 * rawnand_ops.c
 *
 * Copyright (C) 2019 Allwinner.
 * 2019.9.19 cuizhikui<cuizhikui@allwinnertech.com>
 *
 *              eNand
 *       Nand flash driver scan module
 *
 * Copyright(C), 2008-2009, SoftWinners Microelectronic Co., Ltd.
 *      All Rights Reserved
 *
 */

#include <linux/string.h>
#include "rawnand_ops.h"
#include "../nand_boot.h"
#include "rawnand_chip.h"
#include "controller/ndfc_ops.h"
#include "rawnand.h"
#include "rawnand_base.h"
#include "rawnand_cfg.h"
#include "rawnand_debug.h"
#include "rawnand_ids.h"
#include "rawnand_readretry.h"
#include "../../nand_osal_uboot.h"


struct nand_phy_write_lsb_cache nand_phy_w_cache[NAND_OPEN_BLOCK_CNT] = {
    {0},
    {0},
};

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_erase_block_start(struct _nand_physic_op_par *npo)
{
	int ret;
	unsigned int row_addr = 0, col_addr = 0;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct nand_controller_info *nctri = nci->nctri;
	struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq;

	//RAWNAND_DBG("%s: ch: %d  chip: %d/%d  block: %d/%d \n", __func__, nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip);

	if ((nci->nctri_chip_no >= nctri->chip_cnt) || (npo->block >= nci->blk_cnt_per_chip)) {
		RAWNAND_ERR("fatal err -0, wrong input parameter, ch: %d  chip: %d/%d  block: %d/%d \n", nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip);
		return ERR_NO_10;
	}
	//wait nand ready before erase
	nand_read_chip_status_ready(nci);

	nand_enable_chip(nci);

	ndfc_clean_cmd_seq(cmd_seq);

	// cmd1: 0x60
	cmd_seq->cmd_type = CMD_TYPE_NORMAL;
	cmd_seq->nctri_cmd[0].cmd = CMD_ERASE_CMD1;
	cmd_seq->nctri_cmd[0].cmd_valid = 1;
	cmd_seq->nctri_cmd[0].cmd_send = 1;

	row_addr = get_row_addr(nci->page_offset_for_next_blk, npo->block, npo->page);
	if (nci->npi->operation_opt & NAND_WITH_TWO_ROW_ADR) {
		cmd_seq->nctri_cmd[0].cmd_acnt = 2;
		fill_cmd_addr(col_addr, 0, row_addr, 2, cmd_seq->nctri_cmd[0].cmd_addr);
	} else {
		cmd_seq->nctri_cmd[0].cmd_acnt = 3;
		fill_cmd_addr(col_addr, 0, row_addr, 3, cmd_seq->nctri_cmd[0].cmd_addr);
	}

	// cmd2: 0xD0
	cmd_seq->nctri_cmd[1].cmd = CMD_ERASE_CMD2;
	cmd_seq->nctri_cmd[1].cmd_valid = 1;
	cmd_seq->nctri_cmd[1].cmd_send = 1;
	cmd_seq->nctri_cmd[1].cmd_wait_rb = 0;

	ret = ndfc_execute_cmd(nci->nctri, cmd_seq);

	nand_disable_chip(nci);

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_erase_block(struct _nand_physic_op_par *npo)
{
	int ret;
	//unsigned char status;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);

	ret = generic_erase_block_start(npo);
	if (ret != 0) {
		RAWNAND_ERR("erase_block wrong1\n");
		return ret;
	}

	ret = nand_read_chip_status_ready(nci);
	if (ret != 0) {
		RAWNAND_ERR("erase_block wrong2\n");
	}

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_read_page_start(struct _nand_physic_op_par *npo)
{
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq;
	u32 row_addr = 0, col_addr = 0;
	u32 def_spare[32];
	int ret;
	u32 ecc_block, sect_bitmap = 0;

	if (npo->sect_bitmap % 2)
		npo->sect_bitmap += 1;

	if (npo->sect_bitmap == 0 || npo->sect_bitmap > nci->sector_cnt_per_page)
	ecc_block = nci->sector_cnt_per_page / 2;
	else
		ecc_block = npo->sect_bitmap / 2;
	sect_bitmap = ((unsigned int)1 << (ecc_block - 1)) | (((unsigned int)1 << (ecc_block - 1)) - 1);

	//wait nand ready before read
	nand_read_chip_status_ready(nci);

	//set ecc mode & randomize
	ndfc_set_ecc_mode(nci->nctri, nci->ecc_mode);
	ndfc_enable_ecc(nci->nctri, 1, nci->randomizer);
	if (nci->randomizer) {
		ndfc_set_rand_seed(nci->nctri, npo->page);
		ndfc_enable_randomize(nci->nctri);
	}

	nand_enable_chip(nci);
	ndfc_clean_cmd_seq(cmd_seq);

	//command
	set_default_batch_read_cmd_seq(cmd_seq);
	nci->nctri->random_addr_num = nci->random_addr_num;
	nci->nctri->random_cmd2_send_flag = nci->random_cmd2_send_flag;

	//address
	row_addr = get_row_addr(nci->page_offset_for_next_blk, npo->block, npo->page);
	if (nci->npi->operation_opt & NAND_WITH_TWO_ROW_ADR) {
		cmd_seq->nctri_cmd[0].cmd_acnt = 4;
		fill_cmd_addr(col_addr, 2, row_addr, 2, cmd_seq->nctri_cmd[0].cmd_addr);
	} else {
		cmd_seq->nctri_cmd[0].cmd_acnt = 5;
		fill_cmd_addr(col_addr, 2, row_addr, 3, cmd_seq->nctri_cmd[0].cmd_addr);
	}

	//data
	cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1;
	if (npo->mdata != NULL) {
		cmd_seq->nctri_cmd[0].cmd_swap_data = 1;
		cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 1;
	} else {
		//don't swap main data with host memory
		cmd_seq->nctri_cmd[0].cmd_swap_data = 0;
		cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0;
	}
	cmd_seq->nctri_cmd[0].cmd_direction = 0; //read
	cmd_seq->nctri_cmd[0].cmd_mdata_addr = npo->mdata;
	/*
	 *cmd_seq->nctri_cmd[0].cmd_mdata_len = nci->sector_cnt_per_page << 9;
	 *cmd_seq->nctri_cmd[0].cmd_data_block_mask = full_bitmap;
	 */
	cmd_seq->nctri_cmd[0].cmd_mdata_len = ecc_block << 10;
	cmd_seq->nctri_cmd[0].cmd_data_block_mask = sect_bitmap;

	if ((npo->mdata == NULL) && (npo->sdata != NULL) && (npo->sect_bitmap == 0)) {
		if (nci->sector_cnt_per_page > 4) {
			cmd_seq->nctri_cmd[0].cmd_mdata_len = 2 << 9; //12 byte spare data in 1K
			cmd_seq->nctri_cmd[0].cmd_data_block_mask = 0x1;
		} else {
			cmd_seq->nctri_cmd[0].cmd_mdata_len = 4 << 9;
			cmd_seq->nctri_cmd[0].cmd_data_block_mask = 0x3;
		}
	}

	ndfc_set_user_data_len_cfg(nci->nctri, nci->sdata_bytes_per_page);
	ndfc_set_user_data_len(nci->nctri);
	memset(def_spare, 0x99, 128);
	ndfc_set_spare_data(nci->nctri, (u8 *)def_spare, nci->sdata_bytes_per_page);
	ret = batch_cmd_io_send(nci->nctri, cmd_seq);
	if (ret) {
		RAWNAND_ERR("read page start, batch cmd io send error:chip:%d block:%d page:%d sect_bitmap:%d mdata:%x slen:%d: !\n", npo->chip, npo->block, npo->page, npo->sect_bitmap, npo->mdata, npo->slen);

		nand_disable_chip(nci);

		return ret;
	}

	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_read_page_end_not_retry(struct _nand_physic_op_par *npo)
{
	s32 ecc_sta = 0, ret = 0;
	uchar spare[64];
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq;

	ret = _batch_cmd_io_wait(nci->nctri, cmd_seq);
	if (ret) {
		RAWNAND_ERR("read page end, batch cmd io wait error:chip:%d block:%d page:%d sect_bitmap:%d mdata:%x slen:%d: !\n", npo->chip, npo->block, npo->page, npo->sect_bitmap, npo->mdata, npo->slen);
		goto ERROR;
	}

	//check ecc
	ecc_sta = ndfc_check_ecc(nci->nctri, nci->sector_cnt_per_page >> nci->ecc_sector);
	//get spare data
	ndfc_get_spare_data(nci->nctri, (u8 *)spare, nci->sdata_bytes_per_page);

	if (npo->slen != 0) {
		memcpy(npo->sdata, spare, npo->slen);
	}
	//update ecc status and spare data
	//RAWNAND_DBG("npo: 0x%x %d 0x%x\n",npo, npo->slen, npo->sdata);
	ret = ndfc_update_ecc_sta_and_spare_data(npo, ecc_sta, spare);

ERROR:
	//disable ecc mode & randomize
	ndfc_disable_ecc(nci->nctri);
	if (nci->randomizer) {
		ndfc_disable_randomize(nci->nctri);
	}
	nand_disable_chip(nci);

	return ret; //ecc status
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_read_page_end(struct _nand_physic_op_par *npo)
{
	int ret;

	ret = df_read_page_end.read_page_end(npo);

	if (ret == ECC_LIMIT) {
		RAWNAND_DBG("read page ecc limit,chip=%d block=%d page=%d\n", npo->chip, npo->block, npo->page);
	} else if (ret != 0) {
		RAWNAND_ERR("ecc err!read page, read page end error %d,chip=%d block=%d page=%d\n", ret, npo->chip, npo->block, npo->page);
	} else {
		;
	}

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_read_page(struct _nand_physic_op_par *npo)
{
	int ret = 0;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct nand_controller_info *nctri = nci->nctri;

	/*RAWNAND_DBG("%s: ch: %d  chip: %d/%d  block: %d/%d  page: %d/%d\n", __func__, nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip, npo->page, nci->page_cnt_per_blk);*/

	if ((nci->nctri_chip_no >= nctri->chip_cnt) || (npo->block >= nci->blk_cnt_per_chip) || (npo->page >= nci->page_cnt_per_blk)) {
		RAWNAND_ERR("fatal err -0, wrong input parameter, ch: %d  chip: %d/%d  block: %d/%d  page: %d/%d\n", nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip, npo->page, nci->page_cnt_per_blk);
		return ERR_NO_11;
	}

	if ((npo->mdata == NULL) && (npo->sdata == NULL) && (npo->sect_bitmap)) {
		RAWNAND_ERR("fatal err -1, wrong input parameter, mdata is NULL, sdata is NULL, sect_bitmap: 0x%x\n", npo->sect_bitmap);
		return ERR_NO_12;
	}

	if ((npo->mdata == NULL) && (npo->sdata == NULL) && (npo->sect_bitmap == 0)) {
		//RAWNAND_DBG("warning -0, mdata: 0x%08x  sdata: 0x%08x  sect_bitmap: 0x%x\n",npo->mdata, npo->sdata, npo->sect_bitmap);
		return 0;
	}

	ret = generic_read_page_start(npo);
	if (ret) {
		RAWNAND_ERR("read page, read page start error %d\n", ret);
		return ret;
	}

	ret = generic_read_page_end(npo);

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_write_page_start(struct _nand_physic_op_par *npo, int plane_no)
{
	uchar spare[64];
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq;
	u32 row_addr = 0, col_addr = 0;
	int dummy_byte;
	unsigned int ecc_block, sect_bitmap = 0;
	int ret;

	if (npo->mdata == NULL) {
		RAWNAND_ERR("write page start, input parameter error!\n");
		return ERR_NO_13;
	}

	if (npo->sect_bitmap == 0 || npo->sect_bitmap > nci->sector_cnt_per_page)
		ecc_block = nci->sector_cnt_per_page / 2;
	else
		ecc_block = npo->sect_bitmap / 2;
	sect_bitmap = ((unsigned int)1 << (ecc_block - 1)) | (((unsigned int)1 << (ecc_block - 1)) - 1);
	if ((plane_no == 0) || (plane_no == 1))
		//wait nand ready before write
		nand_read_chip_status_ready(nci);
#if 0
	if ((nci->driver_no == 1) && (hynix16nm_read_retry_mode == 0x4) && (nci->retry_count != 0)) {
		hynix16nm_set_default_param(nci);
		nci->retry_count = 0;
	}
#endif
	//    RAWNAND_DBG("chip:%d block:%d page: %d buf:0x%x%x spare:0x%x%x%x \n",npo->chip,npo->block,npo->page,npo->mdata[0],npo->mdata[1],npo->sdata[2],npo->sdata[3],npo->sdata[4]);

	//    if((nci->driver_no == 2)&&((0x2 == hynix20nm_read_retry_mode)||(0x3 == hynix20nm_read_retry_mode))&&(nci->retry_count != 0))
	//    {
	//        hynix20nm_set_default_param(nci);
	//        nci->retry_count = 0;
	//	}

	//get spare data
	memset(spare, 0xff, 64);
	if (npo->slen != 0) {
		memcpy(spare, npo->sdata, npo->slen);
	}

	nand_enable_chip(nci);
	ndfc_clean_cmd_seq(cmd_seq);

	ndfc_set_spare_data(nci->nctri, (u8 *)spare, nci->sdata_bytes_per_page);

	//set ecc mode & randomize
	if (nci->randomizer) {
		ndfc_set_rand_seed(nci->nctri, npo->page);
		ndfc_enable_randomize(nci->nctri);
	}
	ndfc_set_ecc_mode(nci->nctri, nci->ecc_mode);
	ndfc_enable_ecc(nci->nctri, 1, nci->randomizer);

	nci->nctri->current_op_type = 1;
	dummy_byte = get_dummy_byte(nci->nand_real_page_size, nci->ecc_mode, nci->sector_cnt_per_page / 2, nci->sdata_bytes_per_page);
	if (dummy_byte > 0) {
		ndfc_set_dummy_byte(nci->nctri, dummy_byte);
		ndfc_enable_dummy_byte(nci->nctri);
	}

	nci->nctri->random_addr_num = nci->random_addr_num;
	nci->nctri->random_cmd2_send_flag = nci->random_cmd2_send_flag;
	if (nci->random_cmd2_send_flag) {
		nci->nctri->random_cmd2 = get_random_cmd2(npo);
	}

	//command
	if (plane_no == 0) {
		set_default_batch_write_cmd_seq(cmd_seq, CMD_WRITE_PAGE_CMD1, CMD_WRITE_PAGE_CMD2);
	} else if (plane_no == 1) {
		//set_default_batch_write_cmd_seq(cmd_seq,CMD_WRITE_PAGE_CMD1,0x11);
		set_default_batch_write_cmd_seq(cmd_seq,
						nci->opt_phy_op_par->instr.multi_plane_write_instr[0],
						nci->opt_phy_op_par->instr.multi_plane_write_instr[1]);
		//set_default_batch_write_cmd_seq(cmd_seq,CMD_WRITE_PAGE_CMD1,CMD_WRITE_PAGE_CMD2);
	} else {
		//set_default_batch_write_cmd_seq(cmd_seq,0x81,CMD_WRITE_PAGE_CMD2);
		set_default_batch_write_cmd_seq(cmd_seq,
						nci->opt_phy_op_par->instr.multi_plane_write_instr[2],
						nci->opt_phy_op_par->instr.multi_plane_write_instr[3]);
		//set_default_batch_write_cmd_seq(cmd_seq,CMD_WRITE_PAGE_CMD1,CMD_WRITE_PAGE_CMD2);
	}

	//address
	row_addr = get_row_addr(nci->page_offset_for_next_blk, npo->block, npo->page);
	if (nci->npi->operation_opt & NAND_WITH_TWO_ROW_ADR) {
		cmd_seq->nctri_cmd[0].cmd_acnt = 4;
		fill_cmd_addr(col_addr, 2, row_addr, 2, cmd_seq->nctri_cmd[0].cmd_addr);
	} else {
		cmd_seq->nctri_cmd[0].cmd_acnt = 5;
		fill_cmd_addr(col_addr, 2, row_addr, 3, cmd_seq->nctri_cmd[0].cmd_addr);
	}

	//data
	cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1;
	cmd_seq->nctri_cmd[0].cmd_swap_data = 1;
	cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 1;
	cmd_seq->nctri_cmd[0].cmd_direction = 1; //write
	//   cmd_seq->nctri_cmd[0].cmd_mdata_addr = npo->mdata;
	cmd_seq->nctri_cmd[0].cmd_mdata_addr = npo->mdata;
	cmd_seq->nctri_cmd[0].cmd_data_block_mask = sect_bitmap;
	/*cmd_seq->nctri_cmd[0].cmd_mdata_len = nci->sector_cnt_per_page << 9;*/
	cmd_seq->nctri_cmd[0].cmd_mdata_len = ecc_block << 10;

	ndfc_set_user_data_len_cfg(nci->nctri, nci->sdata_bytes_per_page);
	ndfc_set_user_data_len(nci->nctri);

	ret = batch_cmd_io_send(nci->nctri, cmd_seq);
	if (ret) {
		RAWNAND_ERR("read2 page start, batch cmd io send error:chip:%d block:%d page:%d sect_bitmap:%d mdata:%x slen:%d: !\n", npo->chip, npo->block, npo->page, npo->sect_bitmap, npo->mdata, npo->slen);
		nand_disable_chip(nci);
		return ret;
	}

	return 0;
}

int generic_write_page_end(struct _nand_physic_op_par *npo)
{
	s32 ret = 0;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq;

	ret = _batch_cmd_io_wait(nci->nctri, cmd_seq);
	if (ret) {
		RAWNAND_ERR("write page end, batch cmd io wait error:chip:%d block:%d page:%d sect_bitmap:%d mdata:%x slen:%d: !\n", npo->chip, npo->block, npo->page, npo->sect_bitmap, npo->mdata, npo->slen);
	}

	//disable ecc mode & randomize
	ndfc_disable_ecc(nci->nctri);
	if (nci->randomizer) {
		ndfc_disable_randomize(nci->nctri);
	}

	ndfc_set_dummy_byte(nci->nctri, 0);
	ndfc_disable_dummy_byte(nci->nctri);

	nci->nctri->random_cmd2_send_flag = 0;
	nci->nctri->current_op_type = 0;

	nand_disable_chip(nci);
	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_write_page(struct _nand_physic_op_par *npo)
{
	int ret = 0;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct nand_controller_info *nctri = nci->nctri;

	/*RAWNAND_DBG("%s: ch: %d  chip: %d/%d  block: %d/%d  page: %d/%d\n", __func__, nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip, npo->page, nci->page_cnt_per_blk);*/

	if ((nci->nctri_chip_no >= nctri->chip_cnt) || (npo->block >= nci->blk_cnt_per_chip) || (npo->page >= nci->page_cnt_per_blk)) {
		RAWNAND_ERR("wfatal err -0, wrong input parameter, ch: %d  chip: %d/%d  block: %d/%d  page: %d/%d\n", nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip, npo->page, nci->page_cnt_per_blk);
		return ERR_NO_14;
	}

    /*
	 *if (npo->sect_bitmap != nci->sector_cnt_per_page) {
	 *        RAWNAND_ERR("wFatal err -2, wrong input parameter, unaligned write page SectBitmap: 0x%x/0x%x\n", npo->sect_bitmap, nci->sector_cnt_per_page);
	 *        return ERR_NO_15;
	 *}
     */

	ret = generic_write_page_start(npo, 0);
	if (ret) {
		RAWNAND_ERR("write page, write page start error %d\n", ret);
		return ret;
	}

	ret = generic_write_page_end(npo);
	if (ret) {
		RAWNAND_ERR("write page, write page end error %d\n", ret);
		return ret;
	}

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_read_two_plane_page_start(struct _nand_physic_op_par *npo, struct _nand_physic_op_par *npo2)
{

	int ret;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq;
	u32 row_addr = 0, col_addr = 0;

	nand_enable_chip(nci);
	ndfc_clean_cmd_seq(cmd_seq);

	cmd_seq->cmd_type = CMD_TYPE_NORMAL;

	cmd_seq->nctri_cmd[0].cmd_valid = 1;
	cmd_seq->nctri_cmd[0].cmd = nci->opt_phy_op_par->instr.multi_plane_read_instr[0]; //multi_plane_read_instr_cmd[0];
	cmd_seq->nctri_cmd[0].cmd_send = 1;
	row_addr = get_row_addr(nci->page_offset_for_next_blk, npo->block, npo->page);
	if (nci->npi->operation_opt & NAND_WITH_TWO_ROW_ADR) {
		cmd_seq->nctri_cmd[0].cmd_acnt = 2;
		fill_cmd_addr(col_addr, 0, row_addr, 2, cmd_seq->nctri_cmd[0].cmd_addr);
	} else {
		cmd_seq->nctri_cmd[0].cmd_acnt = 3;
		fill_cmd_addr(col_addr, 0, row_addr, 3, cmd_seq->nctri_cmd[0].cmd_addr);
	}

	cmd_seq->nctri_cmd[1].cmd_valid = 1;
	cmd_seq->nctri_cmd[1].cmd = nci->opt_phy_op_par->instr.multi_plane_read_instr[2]; //multi_plane_read_instr_cmd[0];
	cmd_seq->nctri_cmd[1].cmd_send = 1;
	row_addr = get_row_addr(nci->page_offset_for_next_blk, npo2->block, npo2->page);
	if (nci->npi->operation_opt & NAND_WITH_TWO_ROW_ADR) {
		cmd_seq->nctri_cmd[1].cmd_acnt = 2;
		fill_cmd_addr(col_addr, 0, row_addr, 2, cmd_seq->nctri_cmd[1].cmd_addr);
	} else {
		cmd_seq->nctri_cmd[1].cmd_acnt = 3;
		fill_cmd_addr(col_addr, 0, row_addr, 3, cmd_seq->nctri_cmd[1].cmd_addr);
	}

	cmd_seq->nctri_cmd[2].cmd_valid = 1;
	cmd_seq->nctri_cmd[2].cmd = nci->opt_phy_op_par->instr.multi_plane_read_instr[3];
	cmd_seq->nctri_cmd[2].cmd_send = 1;
	cmd_seq->nctri_cmd[2].cmd_wait_rb = 1;

	ret = ndfc_execute_cmd(nci->nctri, cmd_seq);
	if (ret) {
		RAWNAND_ERR("read_two_plane_page_start failed!\n");
	}

	nand_disable_chip(nci);

	return ret;
}

int generic_read_two_plane_page_end(struct _nand_physic_op_par *npo)
{
	int ret = 0;
	//    int ecc_sta = 0;
	//    struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	//    struct _nctri_cmd_seq *cmd_seq = &nci->nctri->nctri_cmd_seq;
	//    u32 row_addr = 0, col_addr = 0;
	//    u32 def_spare[32];
	//
	//    nand_enable_chip(nci);
	//
	//    //set ecc mode & randomize
	//    ndfc_set_ecc_mode(nci->nctri, nci->ecc_mode);
	//    ndfc_enable_ecc(nci->nctri, 1, nci->randomizer);
	//    if (nci->randomizer)
	//    {
	//    	ndfc_set_rand_seed(nci->nctri, npo->page);
	//    	ndfc_enable_randomize(nci->nctri);
	//    }
	//
	//    ndfc_clean_cmd_seq(cmd_seq);
	//    cmd_seq->cmd_type = CMD_TYPE_NORMAL;
	//
	//    cmd_seq->nctri_cmd[0].cmd_valid = 1;
	//    cmd_seq->nctri_cmd[0].cmd = 0x00;
	//    cmd_seq->nctri_cmd[0].cmd_send = 1;
	//    row_addr = get_row_addr(nci->page_offset_for_next_blk, npo->block, npo->page);
	//    cmd_seq->nctri_cmd[0].cmd_acnt = 5;
	//    fill_cmd_addr(col_addr, 2, row_addr, 3, cmd_seq->nctri_cmd[0].cmd_addr);
	//    ret = ndfc_execute_cmd(nci->nctri, cmd_seq);
	//	if (ret)
	//    {
	//		RAWNAND_ERR("read_two_plane_page_start failed!\n");
	//		nand_disable_chip(nci);
	//		return ret;
	//	}
	//
	//    //command
	//    ndfc_clean_cmd_seq(cmd_seq);
	//
	//	cmd_seq->cmd_type = CMD_TYPE_BATCH;
	//	cmd_seq->ecc_layout = ECC_LAYOUT_INTERLEAVE;
	//
	//	cmd_seq->nctri_cmd[0].cmd = 0x05;
	//	cmd_seq->nctri_cmd[1].cmd = 0xe0;
	//	cmd_seq->nctri_cmd[2].cmd = 0x05;
	//	cmd_seq->nctri_cmd[3].cmd = 0xe0;
	//
	//    //address
	//    row_addr = get_row_addr(nci->page_offset_for_next_blk, npo->block, npo->page);
	//    cmd_seq->nctri_cmd[0].cmd_acnt = 2;
	//    fill_cmd_addr(col_addr, 2, row_addr, 0, cmd_seq->nctri_cmd[0].cmd_addr);
	//
	//    //data
	//	cmd_seq->nctri_cmd[0].cmd_trans_data_nand_bus = 1;
	//	if (npo->mdata != NULL)
	//	{
	//		cmd_seq->nctri_cmd[0].cmd_swap_data = 1;
	//		cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 1;
	//	}
	//	else
	//    {
	//		//don't swap main data with host memory
	//		cmd_seq->nctri_cmd[0].cmd_swap_data = 0;
	//		cmd_seq->nctri_cmd[0].cmd_swap_data_dma = 0;
	//	}
	//	cmd_seq->nctri_cmd[0].cmd_direction = 0; //read
	//	cmd_seq->nctri_cmd[0].cmd_mdata_addr = npo->mdata;
	//	cmd_seq->nctri_cmd[0].cmd_mdata_len = nci->sector_cnt_per_page << 9;
	//
	//	memset(def_spare, 0x99, 128);
	//	ndfc_set_spare_data(nci->nctri, (u32*)def_spare, MAX_ECC_BLK_CNT);
	//	ret = rawnand_diff_func->cmd_ops.batch_cmd_io_send(nci->nctri, cmd_seq);
	//	if(ret)
	//	{
	//		RAWNAND_ERR("read3 page start, batch cmd io send error:chip:%d block:%d page:%d sect_bitmap:%d mdata:%x slen:%d: !\n",npo->chip,npo->block,npo->page,npo->sect_bitmap,npo->mdata,npo->slen);
	//		nand_disable_chip(nci);
	//		return ret;
	//	}
	//    ret = rawnand_diff_func->batch_cmd_io_wait(nci->nctri, cmd_seq);
	//	if (ret)
	//	{
	//		RAWNAND_ERR("read2 page end, batch cmd io wait error:chip:%d block:%d page:%d sect_bitmap:%d mdata:%x slen:%d: !\n",npo->chip,npo->block,npo->page,npo->sect_bitmap,npo->mdata,npo->slen);
	//	}
	//
	//	//check ecc
	//	ecc_sta = rawnand_diff_func->ndfc_check_ecc(nci->nctri, nci->sector_cnt_per_page>>nci->ecc_sector);
	//	//get spare data
	//	ndfc_get_spare_data(nci->nctri, (u32*)def_spare, nci->sector_cnt_per_page>>nci->ecc_sector);
	//
	//	if (npo->slen != 0)
	//	{
	//		memcpy(npo->sdata, def_spare, npo->slen);
	//	}
	//    //update ecc status and spare data
	//    //RAWNAND_DBG("npo: 0x%x %d 0x%x\n",npo, npo->slen, npo->sdata);
	//    ret = ndfc_update_ecc_sta_and_spare_data(npo, ecc_sta, def_spare);
	//
	//    //disable ecc mode & randomize
	//    ndfc_disable_ecc(nci->nctri);
	//    if (nci->randomizer)
	//    {
	//    	ndfc_disable_randomize(nci->nctri);
	//    }
	//
	//    nand_disable_chip(nci);

	return ret;
}

int generic_read_two_plane_page(struct _nand_physic_op_par *npo)
{

	int ret = 0;
	int ret0 = 0, ret1 = 0;
	uchar spare[64];
	int i;
	int len_off = 0;
	struct _nand_physic_op_par npo1;
	struct _nand_physic_op_par npo2;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	//struct _nctri_cmd_seq* cmd_seq = &nci->nctri->nctri_cmd_seq;

	npo1.chip = npo->chip;
	npo1.block = (npo->block << 1);
	npo1.page = npo->page;
	/*npo1.sect_bitmap = nci->sector_cnt_per_page;*/
	npo1.sect_bitmap = min(nci->sector_cnt_per_page, npo->sect_bitmap);;
	npo1.mdata = npo->mdata;
	npo1.sdata = npo->sdata;
	npo1.slen = npo->slen;
	if ((npo1.slen == 0) || (npo1.sdata == NULL)) {
		npo1.sdata = spare;
		npo1.slen = nci->sdata_bytes_per_page;
	}

	npo2.chip = npo->chip;
	npo2.block = (npo->block << 1) + 1;
	npo2.page = npo->page;
	/*npo2.sect_bitmap = nci->sector_cnt_per_page;*/

	len_off = npo->sect_bitmap - nci->sector_cnt_per_page;

	npo2.sect_bitmap = (len_off > 0) ? len_off : 0;

	if (npo->mdata != NULL) {
		npo2.mdata = npo->mdata + (nci->sector_cnt_per_page << 9);
	} else {
		npo2.mdata = NULL;
	}
	npo2.sdata = spare;
	npo2.slen = npo->slen;
	if ((npo2.slen == 0) || (npo2.sdata == NULL)) {
		npo2.sdata = spare;
		npo2.slen = nci->sdata_bytes_per_page;
	}


	if ((npo->mdata == NULL) && (npo->sect_bitmap == 0)) {
		npo1.sect_bitmap = 0;
		npo2.sect_bitmap = 0;
		npo2.sdata = NULL;
		npo2.slen = 0;
	}

	if (nci->sector_cnt_per_page == 4) {
		npo1.sdata = spare;
		npo2.sdata = &spare[8];
		npo2.slen = nci->sdata_bytes_per_page;
	}

	//    ret |= generic_read_two_plane_page_start(&npo1,&npo2);
	//    ret |= generic_read_two_plane_page_end(&npo1);
	//    ret |= generic_read_two_plane_page_end(&npo2);

	ret0 = generic_read_page(&npo1);
	ret1 = generic_read_page(&npo2);

	if (nci->sector_cnt_per_page == 4) {
		for (i = 0; i < 16; i++) {
			if (i < 8) {
				*((unsigned char *)npo->sdata + i) = spare[i];
			} else if (i == 15) {
				*((unsigned char *)npo->sdata + i) = 0xff;
			} else {
				*((unsigned char *)npo->sdata + i) = spare[i + 1];
			}
		}
	}

	if ((ret0 == ERR_ECC) || (ret1 == ERR_ECC))
		ret = ERR_ECC;
	else if ((ret0 == ECC_LIMIT) || (ret1 == ECC_LIMIT))
		ret = ECC_LIMIT;
	else
		ret = ret0 | ret1;

	return ret;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_write_two_plane_page(struct _nand_physic_op_par *npo)
{

	int ret = 0;
	uchar spare[64];
	int i;
	struct _nand_physic_op_par npo1;
	struct _nand_physic_op_par npo2;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	//struct _nctri_cmd_seq* cmd_seq = &nci->nctri->nctri_cmd_seq;

	npo1.chip = npo->chip;
	npo1.block = npo->block << 1;
	npo1.page = npo->page;
	npo1.sect_bitmap = nci->sector_cnt_per_page;
	npo1.mdata = npo->mdata;
	npo1.sdata = npo->sdata;
	npo1.slen = npo->slen;

	npo2.chip = npo->chip;
	npo2.block = (npo->block << 1) + 1;
	npo2.page = npo->page;
	npo2.sect_bitmap = nci->sector_cnt_per_page;
	npo2.mdata = npo->mdata + (nci->sector_cnt_per_page << 9);
	npo2.sdata = npo->sdata;
	npo2.slen = npo->slen;

	if (nci->sector_cnt_per_page == 4) {
		for (i = 0; i < 16; i++) {
			if (i < 8) {
				spare[i] = *((unsigned char *)npo->sdata + i);
			} else if (i == 8) {
				spare[i] = 0xff;
			} else {
				spare[i] = *((unsigned char *)npo->sdata + i - 1);
			}
		}
		npo1.sdata = spare;
		npo2.sdata = &spare[8];
	}
	//    if(nci->driver_no == 2)
	//    {
	//        ret |= generic_write_page_start(&npo1,0);
	//        ret |= generic_write_page_end(&npo1);
	//
	//        ret |= generic_write_page_start(&npo2,0);
	//        ret |= generic_write_page_end(&npo2);
	//    }
	//    else
	{
		ret |= generic_write_page_start(&npo1, 1);
		ret |= generic_write_page_end(&npo1);

		ret |= generic_write_page_start(&npo2, 2);
		ret |= generic_write_page_end(&npo2);
	}

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:good block  -1:bad block
 *Note         :
 *****************************************************************************/
int generic_bad_block_check(struct _nand_physic_op_par *npo)
{
	int num, start_page, i;
	unsigned char spare[64];
	struct _nand_physic_op_par lnpo;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct nand_controller_info *nctri = nci->nctri;

	/*RAWNAND_DBG("%s: ch: %d  chip: %d/%d  block: %d/%d \n", __func__, nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip);*/

	if ((nci->nctri_chip_no >= nctri->chip_cnt) || (npo->block >= nci->blk_cnt_per_chip)) {
		RAWNAND_ERR("cfatal err -0, wrong input parameter, ch: %d  chip: %d/%d  block: %d/%d \n", nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip);
		return ERR_NO_16;
	}

	lnpo.chip = npo->chip;
	lnpo.block = npo->block;
	lnpo.mdata = NULL;
	lnpo.sdata = spare;
	lnpo.sect_bitmap = npo->sect_bitmap;
	lnpo.slen = nci->sdata_bytes_per_page;
	if (nci->bad_block_flag_position == FIRST_PAGE) {
		//the bad block flag is in the first page, same as the logical information, just read 1st page is ok
		start_page = 0;
		num = 1;
	} else if (nci->bad_block_flag_position == FIRST_TWO_PAGES) {
		//the bad block flag is in the first page or the second page, need read the first page and the second page
		start_page = 0;
		num = 2;
	} else if (nci->bad_block_flag_position == LAST_PAGE) {
		//the bad block flag is in the last page, need read the first page and the last page
		start_page = nci->page_cnt_per_blk - 1;
		num = 1;
	} else if (nci->bad_block_flag_position == LAST_TWO_PAGES) {
		//the bad block flag is in the last 2 page, so, need read the first page, the last page and the last-1 page
		start_page = nci->page_cnt_per_blk - 2;
		num = 2;
	} else {
		RAWNAND_ERR("bad block check, unknown bad block flag position\n");
		return ERR_NO_17;
	}

	//read and check 1st page
	lnpo.page = 0;
	generic_read_page(&lnpo);
	if (lnpo.sdata[0] != 0xff) {
		RAWNAND_ERR("find a bad block: %d %d %d ", lnpo.chip, lnpo.block, lnpo.page);
		RAWNAND_ERR("sdata: %02x %02x %02x %02x \n", lnpo.sdata[0], lnpo.sdata[1], lnpo.sdata[2], lnpo.sdata[3]);
		return -1;
	}

	//read and check other pages
	for (i = 0, lnpo.page = start_page; i < num; i++) {
		generic_read_page(&lnpo);
		if (lnpo.sdata[0] != 0xff) {
			RAWNAND_ERR("find a bad block: %d %d %d ", lnpo.chip, lnpo.block, lnpo.page);
			RAWNAND_ERR("sdata: %02x %02x %02x %02x \n", lnpo.sdata[0], lnpo.sdata[1], lnpo.sdata[2], lnpo.sdata[3]);
			return -1;
		}
		lnpo.page++;
	}

	return 0;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_bad_block_mark(struct _nand_physic_op_par *npo)
{

	int num, start_page, i, ret;
	unsigned char spare[64];
	struct _nand_physic_op_par lnpo;
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);
	struct nand_controller_info *nctri = nci->nctri;
	unsigned char *mbuf = nand_get_temp_buf(nctri->nci->sector_cnt_per_page << 9);
	if (mbuf == NULL) {
		RAWNAND_ERR("bad block mark no memory chip: %d block:%d\n", npo->chip, npo->block);
		return ERR_NO_18;
	}
	//RAWNAND_ERR("%s: ch: %d  chip: %d/%d  block: %d/%d \n", __func__, nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip);

	if ((nci->nctri_chip_no >= nctri->chip_cnt) || (npo->block >= nci->blk_cnt_per_chip)) {
		RAWNAND_ERR("Mfatal err -0, wrong input parameter, chip: %d/%d/%d  block: %d/%d \n", nctri->channel_id, nci->nctri_chip_no, nctri->chip_cnt, npo->block, nci->blk_cnt_per_chip);
		return ERR_NO_18;
	}

	RAWNAND_DBG("bad block mark chip: %d block:%d\n", npo->chip, npo->block);

	lnpo.chip = npo->chip;
	lnpo.block = npo->block;
	lnpo.page = 0;
	lnpo.sect_bitmap = nci->sector_cnt_per_page;
	lnpo.mdata = mbuf;
	lnpo.sdata = spare;
	lnpo.slen = nci->sdata_bytes_per_page;

	if (nci->bad_block_flag_position == FIRST_PAGE) {
		start_page = 0;
		num = 1;
	} else if (nci->bad_block_flag_position == FIRST_TWO_PAGES) {
		start_page = 0;
		num = 2;
	} else if (nci->bad_block_flag_position == LAST_PAGE) {
		start_page = nci->page_cnt_per_blk - 1;
		num = 1;
	} else if (nci->bad_block_flag_position == LAST_TWO_PAGES) {
		start_page = nci->page_cnt_per_blk - 2;
		num = 2;
	} else {
		nand_free_temp_buf(mbuf, nctri->nci->sector_cnt_per_page << 9);
		RAWNAND_ERR("bad block mark, unknown bad block flag position\n");
		return ERR_NO_19;
	}

	ret = generic_erase_block(&lnpo);
	if (ret) {
		nand_free_temp_buf(mbuf, nctri->nci->sector_cnt_per_page << 9);
		RAWNAND_ERR("bad block mark, erase block failed, blk %d, chip %d, ch %d\n", lnpo.block, lnpo.chip, nci->nctri->channel_id);
		return ret;
	}

	memset(lnpo.sdata, 0, 64);
	generic_write_page(&lnpo);

	for (i = 0, lnpo.page = start_page; i < num; i++) {
		if (lnpo.page != 0) {
			lnpo.chip = npo->chip;
			lnpo.block = npo->block;
			lnpo.sect_bitmap = nci->sector_cnt_per_page;
			lnpo.mdata = mbuf;
			lnpo.sdata = spare;
			lnpo.slen = nci->sdata_bytes_per_page;
			generic_write_page(&lnpo);
		}
		lnpo.page++;
	}

	//check bad block flag
	memset(lnpo.sdata, 0xff, 64);

	lnpo.chip = npo->chip;
	lnpo.block = npo->block;
	lnpo.page = 0;
	lnpo.sect_bitmap = nci->sector_cnt_per_page;
	lnpo.mdata = NULL;
	lnpo.sdata = spare;
	lnpo.slen = nci->sdata_bytes_per_page;
	generic_read_page(&lnpo);

	if (lnpo.sdata[0] != 0xff) {
		nand_free_temp_buf(mbuf, nctri->nci->sector_cnt_per_page << 9);
		return 0;
	}

	for (i = 0, lnpo.page = start_page; i < num; i++) {
		lnpo.chip = npo->chip;
		lnpo.block = npo->block;
		lnpo.sect_bitmap = nci->sector_cnt_per_page;
		lnpo.mdata = NULL;
		lnpo.sdata = spare;
		lnpo.slen = nci->sdata_bytes_per_page;
		generic_read_page(&lnpo);

		if (lnpo.sdata[0] != 0xff) {
			nand_free_temp_buf(mbuf, nctri->nci->sector_cnt_per_page << 9);
			return 0;
		}
		lnpo.page++;
	}

	nand_free_temp_buf(mbuf, nctri->nci->sector_cnt_per_page << 9);

	return ERR_NO_20;
}

int nand_phy_get_page_type(unsigned int page)
{
	/*
	   for SPECTEK L04a 3D nand
	   page type:1-> independent page,
	   page type:2-> lsb page
	   page type:3-> msb page
	   */
	if ((page <= 15) || (page >= 496))
		return 1;

	if (page % 2 == 0)
		return 2;

	return 3;
}

int nand_phy_low_page_write_cache_set(struct _nand_physic_op_par *npo,
				      unsigned int two_plane)
{
	int i = 0;

	for (i = 0; i < NAND_OPEN_BLOCK_CNT; i++) {
		if (nand_phy_w_cache[i].cache_use_status == 0) {
			/*get a new lsb page cache and backup for L04a*/
			nand_phy_w_cache[i].cache_use_status = 1;
			nand_phy_w_cache[i].tmp_npo.chip = npo->chip;
			nand_phy_w_cache[i].tmp_npo.block = npo->block;
			nand_phy_w_cache[i].tmp_npo.page = npo->page;
			nand_phy_w_cache[i].tmp_npo.sect_bitmap = npo->sect_bitmap;
			nand_phy_w_cache[i].tmp_npo.slen = npo->slen;

			if (nand_phy_w_cache[i].tmp_npo.mdata == NULL) {
				if (two_plane == 1)
					nand_phy_w_cache[i].tmp_npo.mdata =
					    NAND_Malloc(
						2 * (npo->sect_bitmap << 9));
				else
					nand_phy_w_cache[i].tmp_npo.mdata =
					    nand_malloc(npo->sect_bitmap << 9);
			}
			if (nand_phy_w_cache[i].tmp_npo.sdata == NULL) {
				nand_phy_w_cache[i].tmp_npo.sdata = NAND_Malloc(npo->slen);
				memset(nand_phy_w_cache[i].tmp_npo.sdata, 0xff, npo->slen);
			}

			if (npo->mdata) {
				if (two_plane == 1)
					memcpy(
					    nand_phy_w_cache[i].tmp_npo.mdata,
					    npo->mdata,
					    2 * (npo->sect_bitmap << 9));
				else
					memcpy(
					    nand_phy_w_cache[i].tmp_npo.mdata,
					    npo->mdata,
					    (npo->sect_bitmap << 9));
			}

			if (npo->sdata)
				memcpy(nand_phy_w_cache[i].tmp_npo.sdata, npo->sdata, npo->slen);

			return 0;
		}
	}

	RAWNAND_ERR("ERR! no cache buf for lsb page!\n");

	return -1;
}

struct _nand_physic_op_par *nand_phy_low_page_cache_get_for_write(struct _nand_physic_op_par *npo)
{
	int i = 0;

	for (i = 0; i < NAND_OPEN_BLOCK_CNT; i++) {
		if ((nand_phy_w_cache[i].cache_use_status == 1) &&
		    (nand_phy_w_cache[i].tmp_npo.chip == npo->chip) &&
		    ((nand_phy_w_cache[i].tmp_npo.page + 1) == npo->page) &&
		    (nand_phy_w_cache[i].tmp_npo.block == npo->block) &&
		    (nand_phy_w_cache[i].tmp_npo.sect_bitmap == npo->sect_bitmap) &&
		    (nand_phy_w_cache[i].tmp_npo.slen == npo->slen) &&
		    (nand_phy_w_cache[i].tmp_npo.mdata != NULL) &&
		    (nand_phy_w_cache[i].tmp_npo.sdata != NULL)) {

			return &(nand_phy_w_cache[i].tmp_npo);
		}
	}
	RAWNAND_ERR("ERR! Not get LSB write cache!\n");

	return NULL;
}

int nand_phy_low_page_write_cache_cancle(struct _nand_physic_op_par *npo)
{
	int i = 0;

	for (i = 0; i < NAND_OPEN_BLOCK_CNT; i++) {
		if ((nand_phy_w_cache[i].cache_use_status == 1) &&
		    (nand_phy_w_cache[i].tmp_npo.chip == npo->chip) &&
		    (nand_phy_w_cache[i].tmp_npo.block == npo->block) &&
		    (nand_phy_w_cache[i].tmp_npo.page == npo->page)) {

			nand_phy_w_cache[i].cache_use_status = 0;

			return 0;
		}
	}

	return -1;
}

int nand_phy_low_page_cache_get_for_read(struct _nand_physic_op_par *npo,
					 unsigned int two_plane)
{
	int i = 0;

	for (i = 0; i < NAND_OPEN_BLOCK_CNT; i++) {
		if ((nand_phy_w_cache[i].tmp_npo.chip == npo->chip) &&
		    (nand_phy_w_cache[i].tmp_npo.block == npo->block) &&
		    (nand_phy_w_cache[i].tmp_npo.page == npo->page) &&
		    (nand_phy_w_cache[i].tmp_npo.mdata != NULL) &&
		    (nand_phy_w_cache[i].tmp_npo.sdata != NULL)) {

			if (npo->mdata) {
				if (two_plane == 1)
					memcpy(
					    npo->mdata,
					    nand_phy_w_cache[i].tmp_npo.mdata,
					    2 * (npo->sect_bitmap << 9));
				else
					memcpy(
					    npo->mdata,
					    nand_phy_w_cache[i].tmp_npo.mdata,
					    (npo->sect_bitmap << 9));
			}
			if (npo->sdata)
				memcpy(npo->sdata, nand_phy_w_cache[i].tmp_npo.sdata, npo->slen);

			return 0;
		}
	}

	return -1;
}

int generic_rw_page(struct _nand_physic_op_par *npo, unsigned int function, unsigned int two_plane)
{
	int ret = 0;

	if ((function == 0) && (two_plane == 0)) {
		ret |= generic_read_page(npo);
	} else if ((function == 0) && (two_plane == 1)) {
		ret |= generic_read_two_plane_page(npo);
	} else if ((function == 1) && (two_plane == 0)) {
		ret |= generic_write_page(npo);
	} else if ((function == 1) && (two_plane == 1)) {
		ret |= generic_write_two_plane_page(npo);
	} else {
		;
	}

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_use_chip_function(struct _nand_physic_op_par *npo, unsigned int function)
{

	int ret, i;
	struct _nand_physic_op_par lnpo;
	struct nand_chip_info *nci;
	struct nand_super_chip_info *nsci = nsci_get_from_nssi(g_nssi, npo->chip);
	unsigned int chip[8];
	unsigned int block[8];
	unsigned int block_num;

	if (nsci->two_plane == 0) {
		if ((nsci->vertical_interleave == 1) && (nsci->dual_channel == 1)) {
			block_num = 4;
			chip[0] = nsci->d_channel_nci_1->chip_no;
			block[0] = npo->block;

			chip[1] = nsci->d_channel_nci_2->chip_no;
			block[1] = npo->block;

			chip[2] = nsci->v_intl_nci_2->chip_no;
			block[2] = npo->block;

			nci = nci_get_from_nctri(nsci->d_channel_nci_2->nctri, nsci->v_intl_nci_1->chip_no);
			chip[3] = nci->chip_no;
			block[3] = npo->block;
		} else if (nsci->vertical_interleave == 1) {
			block_num = 2;
			chip[0] = nsci->v_intl_nci_1->chip_no;
			block[0] = npo->block;

			chip[1] = nsci->v_intl_nci_2->chip_no;
			block[1] = npo->block;
		} else if (nsci->dual_channel == 1) {
			block_num = 2;
			chip[0] = nsci->d_channel_nci_1->chip_no;
			block[0] = npo->block;

			chip[1] = nsci->d_channel_nci_2->chip_no;
			block[1] = npo->block;
		} else {
			block_num = 1;
			chip[0] = npo->chip;
			block[0] = npo->block;
		}
	} else {
		if ((nsci->vertical_interleave == 1) && (nsci->dual_channel == 1)) {
			block_num = 8;
			chip[0] = nsci->d_channel_nci_1->chip_no;
			block[0] = npo->block << 1;
			chip[1] = nsci->d_channel_nci_1->chip_no;
			block[1] = (npo->block << 1) + 1;

			chip[2] = nsci->d_channel_nci_2->chip_no;
			block[2] = npo->block << 1;
			chip[3] = nsci->d_channel_nci_2->chip_no;
			block[3] = (npo->block << 1) + 1;

			chip[4] = nsci->v_intl_nci_2->chip_no;
			block[4] = npo->block << 1;
			chip[5] = nsci->v_intl_nci_2->chip_no;
			block[5] = (npo->block << 1) + 1;

			nci = nci_get_from_nctri(nsci->d_channel_nci_2->nctri, nsci->v_intl_nci_1->chip_no);
			chip[6] = nci->chip_no;
			block[6] = npo->block << 1;
			chip[7] = nci->chip_no;
			block[7] = (npo->block << 1) + 1;
		} else if (nsci->vertical_interleave == 1) {
			block_num = 4;
			chip[0] = nsci->v_intl_nci_1->chip_no;
			block[0] = npo->block << 1;
			chip[1] = nsci->v_intl_nci_1->chip_no;
			block[1] = (npo->block << 1) + 1;

			chip[2] = nsci->v_intl_nci_2->chip_no;
			block[2] = npo->block << 1;
			chip[3] = nsci->v_intl_nci_2->chip_no;
			block[3] = (npo->block << 1) + 1;
		} else if (nsci->dual_channel == 1) {
			block_num = 4;
			chip[0] = nsci->d_channel_nci_1->chip_no;
			block[0] = npo->block << 1;
			chip[1] = nsci->d_channel_nci_1->chip_no;
			block[1] = (npo->block << 1) + 1;

			chip[2] = nsci->d_channel_nci_2->chip_no;
			block[2] = npo->block << 1;
			chip[3] = nsci->d_channel_nci_2->chip_no;
			block[3] = (npo->block << 1) + 1;
		} else {
			block_num = 2;
			chip[0] = npo->chip;
			block[0] = npo->block << 1;
			chip[1] = npo->chip;
			block[1] = (npo->block << 1) + 1;
		}
	}

	for (i = 0, ret = 0; i < block_num; i++) {
		lnpo.chip = chip[i];
		lnpo.block = block[i];
		lnpo.page = 0;
		if (function == 0) {
			ret |= generic_erase_block(&lnpo);
			//ret |= generic_erase_block_start(&lnpo);
		} else if (function == 1) {
			ret |= generic_bad_block_check(&lnpo);
			if (ret != 0) {
				break;
			}
		} else if (function == 2) {
			ret |= generic_bad_block_mark(&lnpo);
		} else {
			;
		}
	}

	nand_wait_all_rb_ready();

	return ret;
}
/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_rw_use_chip_function(struct _nand_physic_op_par *npo, unsigned int function)
{
	int ret = 0;
	struct _nand_physic_op_par lnpo;
	struct _nand_physic_op_par *low_npo;
	struct nand_super_chip_info *nsci = nsci_get_from_nssi(g_nssi, npo->chip);
	struct nand_chip_info *nci = nci_get_from_nsi(g_nsi, npo->chip);

	unsigned int chip[4];
	unsigned int block[4];
	unsigned int page[4];
	unsigned char *mdata[4];
	unsigned char *sdata[4];
	unsigned int slen[4];
	unsigned int sect_bitmap[4];
	//	unsigned int block_num;
	unsigned char oob_temp[64];
	unsigned int i;

	if ((nsci->dual_channel == 1) && (nsci->two_plane == 1) && (function == 1)) {
		//		block_num = 4;
		chip[0] = nsci->d_channel_nci_1->chip_no;
		block[0] = npo->block << 1;
		page[0] = npo->page;
		mdata[0] = npo->mdata;
		sect_bitmap[0] = nsci->d_channel_nci_1->sector_cnt_per_page;
		sdata[0] = npo->sdata;
		slen[0] = npo->slen;

		chip[1] = nsci->d_channel_nci_2->chip_no;
		block[1] = npo->block << 1;
		page[1] = npo->page;
		mdata[1] = npo->mdata + (nsci->d_channel_nci_1->sector_cnt_per_page << 10);
		sect_bitmap[1] = nsci->d_channel_nci_2->sector_cnt_per_page;
		sdata[1] = npo->sdata;
		slen[1] = npo->slen;

		chip[2] = nsci->d_channel_nci_1->chip_no;
		block[2] = (npo->block << 1) + 1;
		page[2] = npo->page;
		mdata[2] = npo->mdata + (nsci->d_channel_nci_1->sector_cnt_per_page << 9);
		sect_bitmap[2] = nsci->d_channel_nci_1->sector_cnt_per_page;
		sdata[2] = npo->sdata;
		slen[2] = npo->slen;

		chip[3] = nsci->d_channel_nci_2->chip_no;
		block[3] = (npo->block << 1) + 1;
		page[3] = npo->page;
		mdata[3] = npo->mdata + (nsci->d_channel_nci_2->sector_cnt_per_page << 10) + (nsci->d_channel_nci_2->sector_cnt_per_page << 9);
		sect_bitmap[3] = nsci->d_channel_nci_2->sector_cnt_per_page;
		sdata[3] = npo->sdata;
		slen[3] = npo->slen;

		if (nsci->d_channel_nci_1->sector_cnt_per_page == 4) {
			for (i = 0; i < 16; i++) {
				if (i < 8) {
					oob_temp[i] = *((unsigned char *)npo->sdata + i);
				} else if (i == 8) {
					oob_temp[i] = 0xff;
				} else {
					oob_temp[i] = *((unsigned char *)npo->sdata + i - 1);
				}
			}
			sdata[0] = &oob_temp[0];
			sdata[1] = &oob_temp[0];
			sdata[2] = &oob_temp[8];
			sdata[3] = &oob_temp[8];
		}

		lnpo.chip = chip[0];
		lnpo.block = block[0];
		lnpo.page = page[0];
		lnpo.mdata = mdata[0];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[0];
		lnpo.slen = slen[0];
		ret |= generic_write_page_start(&lnpo, 1);

		lnpo.chip = chip[1];
		lnpo.block = block[1];
		lnpo.page = page[1];
		lnpo.mdata = mdata[1];
		lnpo.sect_bitmap = sect_bitmap[1];
		lnpo.sdata = sdata[1];
		lnpo.slen = slen[1];
		ret |= generic_write_page_start(&lnpo, 1);

		lnpo.chip = chip[0];
		lnpo.block = block[0];
		lnpo.page = page[0];
		lnpo.mdata = mdata[0];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[0];
		lnpo.slen = slen[0];
		ret |= generic_write_page_end(&lnpo);

		lnpo.chip = chip[2];
		lnpo.block = block[2];
		lnpo.page = page[2];
		lnpo.mdata = mdata[2];
		lnpo.sect_bitmap = sect_bitmap[2];
		lnpo.sdata = sdata[2];
		lnpo.slen = slen[2];
		ret |= generic_write_page_start(&lnpo, 2);

		lnpo.chip = chip[1];
		lnpo.block = block[1];
		lnpo.page = page[1];
		lnpo.mdata = mdata[1];
		lnpo.sect_bitmap = sect_bitmap[1];
		lnpo.sdata = sdata[1];
		lnpo.slen = slen[1];
		ret |= generic_write_page_end(&lnpo);

		lnpo.chip = chip[3];
		lnpo.block = block[3];
		lnpo.page = page[3];
		lnpo.mdata = mdata[3];
		lnpo.sect_bitmap = sect_bitmap[3];
		lnpo.sdata = sdata[3];
		lnpo.slen = slen[3];
		ret |= generic_write_page_start(&lnpo, 2);

		lnpo.chip = chip[2];
		lnpo.block = block[2];
		lnpo.page = page[2];
		lnpo.mdata = mdata[2];
		lnpo.sect_bitmap = sect_bitmap[2];
		lnpo.sdata = sdata[2];
		lnpo.slen = slen[2];
		ret |= generic_write_page_end(&lnpo);

		lnpo.chip = chip[3];
		lnpo.block = block[3];
		lnpo.page = page[3];
		lnpo.mdata = mdata[3];
		lnpo.sect_bitmap = sect_bitmap[3];
		lnpo.sdata = sdata[3];
		lnpo.slen = slen[3];
		ret |= generic_write_page_end(&lnpo);

		return ret;
	}

	if ((nsci->dual_channel == 1) && (nsci->two_plane == 1) && (function == 0)) {
		//		block_num = 4;
		chip[0] = nsci->d_channel_nci_1->chip_no;
		block[0] = npo->block << 1;
		page[0] = npo->page;
		mdata[0] = npo->mdata;
		sect_bitmap[0] = nsci->d_channel_nci_1->sector_cnt_per_page;
		sdata[0] = npo->sdata;
		slen[0] = npo->slen;

		chip[1] = nsci->d_channel_nci_2->chip_no;
		block[1] = npo->block << 1;
		page[1] = npo->page;
		mdata[1] = npo->mdata + (nsci->d_channel_nci_1->sector_cnt_per_page << 10);
		sect_bitmap[1] = sect_bitmap[0];
		sdata[1] = NULL;
		slen[1] = 0;

		chip[2] = nsci->d_channel_nci_1->chip_no;
		block[2] = (npo->block << 1) + 1;
		page[2] = npo->page;
		mdata[2] = npo->mdata + (nsci->d_channel_nci_1->sector_cnt_per_page << 9);
		sect_bitmap[2] = sect_bitmap[0];
		sdata[2] = NULL;
		slen[2] = 0;

		chip[3] = nsci->d_channel_nci_2->chip_no;
		block[3] = (npo->block << 1) + 1;
		page[3] = npo->page;
		mdata[3] = npo->mdata + (nsci->d_channel_nci_2->sector_cnt_per_page << 10) + (nsci->d_channel_nci_2->sector_cnt_per_page << 9);
		sect_bitmap[3] = sect_bitmap[0];
		sdata[3] = NULL;
		slen[3] = 0;

		if (nsci->d_channel_nci_1->sector_cnt_per_page == 4) {
			sdata[0] = &oob_temp[0];
			sdata[1] = NULL;
			sdata[2] = &oob_temp[8];
			sdata[3] = NULL;
		}

		lnpo.chip = chip[0];
		lnpo.block = block[0];
		lnpo.page = page[0];
		lnpo.mdata = mdata[0];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[0];
		lnpo.slen = slen[0];
		ret |= generic_read_page_start(&lnpo);

		lnpo.chip = chip[1];
		lnpo.block = block[1];
		lnpo.page = page[1];
		lnpo.mdata = mdata[1];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[1];
		lnpo.slen = slen[1];
		if (mdata[0] != NULL)
			ret |= generic_read_page_start(&lnpo);

		lnpo.chip = chip[0];
		lnpo.block = block[0];
		lnpo.page = page[0];
		lnpo.mdata = mdata[0];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[0];
		lnpo.slen = slen[0];
		ret |= generic_read_page_end(&lnpo);

		lnpo.chip = chip[2];
		lnpo.block = block[2];
		lnpo.page = page[2];
		lnpo.mdata = mdata[2];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[2];
		lnpo.slen = slen[2];
		if (mdata[0] != NULL)
			ret |= generic_read_page_start(&lnpo);

		lnpo.chip = chip[1];
		lnpo.block = block[1];
		lnpo.page = page[1];
		lnpo.mdata = mdata[1];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[1];
		lnpo.slen = slen[1];
		if (mdata[0] != NULL)
			ret |= generic_read_page_end(&lnpo);

		lnpo.chip = chip[3];
		lnpo.block = block[3];
		lnpo.page = page[3];
		lnpo.mdata = mdata[3];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[3];
		lnpo.slen = slen[3];
		if (mdata[0] != NULL)
			ret |= generic_read_page_start(&lnpo);

		lnpo.chip = chip[2];
		lnpo.block = block[2];
		lnpo.page = page[2];
		lnpo.mdata = mdata[2];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[2];
		lnpo.slen = slen[2];
		if (mdata[0] != NULL)
			ret |= generic_read_page_end(&lnpo);

		lnpo.chip = chip[3];
		lnpo.block = block[3];
		lnpo.page = page[3];
		lnpo.mdata = mdata[3];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[3];
		lnpo.slen = slen[3];
		if (mdata[0] != NULL)
			ret |= generic_read_page_end(&lnpo);

		if (nsci->d_channel_nci_1->sector_cnt_per_page == 4) {
			for (i = 0; i < 16; i++) {
				if (i < 8) {
					*((unsigned char *)npo->sdata + i) = oob_temp[i];
				} else if (i == 15) {
					*((unsigned char *)npo->sdata + i) = 0xff;
				} else {
					*((unsigned char *)npo->sdata + i) = oob_temp[i + 1];
				}
			}
		}

		return ret;
	}

	if (nsci->dual_channel == 1) {
		//		block_num = 2;
		chip[0] = nsci->d_channel_nci_1->chip_no;
		block[0] = npo->block;
		page[0] = npo->page;
		mdata[0] = npo->mdata;
		sect_bitmap[0] = nsci->d_channel_nci_1->sector_cnt_per_page;
		sdata[0] = npo->sdata;
		slen[0] = npo->slen;

		chip[1] = nsci->d_channel_nci_2->chip_no;
		block[1] = npo->block;
		page[1] = npo->page;
		mdata[1] = npo->mdata + (nsci->d_channel_nci_1->sector_cnt_per_page << 9);
		sect_bitmap[1] = sect_bitmap[0];
		sdata[1] = npo->sdata;
		slen[1] = npo->slen;
		if (function == 0) {
			sdata[1] = NULL;
			slen[1] = 0;
		}

		lnpo.chip = chip[0];
		lnpo.block = block[0];
		lnpo.page = page[0];
		lnpo.mdata = mdata[0];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[0];
		lnpo.slen = slen[0];
		if (function == 1) {
			ret |= generic_write_page_start(&lnpo, 0);
		} else {
			ret |= generic_read_page_start(&lnpo);
		}

		lnpo.chip = chip[1];
		lnpo.block = block[1];
		lnpo.page = page[1];
		lnpo.mdata = mdata[1];
		lnpo.sect_bitmap = sect_bitmap[1];
		lnpo.sdata = sdata[1];
		lnpo.slen = slen[1];
		if (function == 1) {
			ret |= generic_write_page_start(&lnpo, 0);
		} else {
			if (mdata[0] != NULL)
				ret |= generic_read_page_start(&lnpo);
		}

		lnpo.chip = chip[0];
		lnpo.block = block[0];
		lnpo.page = page[0];
		lnpo.mdata = mdata[0];
		lnpo.sect_bitmap = sect_bitmap[0];
		lnpo.sdata = sdata[0];
		lnpo.slen = slen[0];
		if (function == 1) {
			ret |= generic_write_page_end(&lnpo);
		} else {
			ret |= generic_read_page_end(&lnpo);
		}

		lnpo.chip = chip[1];
		lnpo.block = block[1];
		lnpo.page = page[1];
		lnpo.mdata = mdata[1];
		lnpo.sect_bitmap = sect_bitmap[1];
		lnpo.sdata = sdata[1];
		lnpo.slen = slen[1];
		if (function == 1) {
			ret |= generic_write_page_end(&lnpo);
		} else {
			if (mdata[0] != NULL)
				ret |= generic_read_page_end(&lnpo);
		}

		return ret;
	}

	if (nsci->vertical_interleave == 1) {
		if (npo->page & 0x01) {
			lnpo.chip = nsci->v_intl_nci_2->chip_no;
		} else {
			lnpo.chip = nsci->v_intl_nci_1->chip_no;
		}
		lnpo.page = npo->page >> 1;
	} else {
		lnpo.chip = nsci->nci_first->chip_no;
		lnpo.page = npo->page;
	}

	lnpo.block = npo->block;
	lnpo.mdata = npo->mdata;
	/*lnpo.sect_bitmap = nsci->nci_first->sector_cnt_per_page;*/
	lnpo.sect_bitmap = npo->sect_bitmap;
	lnpo.sdata = npo->sdata;
	lnpo.slen = npo->slen;

	if (lnpo.mdata == NULL)
		lnpo.sect_bitmap = 0;

	if ((nci->npi->operation_opt & NAND_PAIRED_PAGE_SYNC) && (function == 1)) {
		if (nand_phy_get_page_type(lnpo.page) == 2) {
			ret = nand_phy_low_page_write_cache_set(
			    &lnpo, nsci->two_plane);
			return ret;
		} else if (nand_phy_get_page_type(lnpo.page) == 3) {
			low_npo = nand_phy_low_page_cache_get_for_write(&lnpo);

			if (low_npo) {
				ret |= generic_rw_page(low_npo, function, nsci->two_plane);
				nand_phy_low_page_write_cache_cancle(low_npo);
			} else {
				RAWNAND_ERR("ERR! Not get low page cache when write uper page\n");
			}
		} else {
			;
		}
	} else if ((nci->npi->operation_opt & NAND_PAIRED_PAGE_SYNC) &&
		   (function == 0)) {
		if ((nand_phy_get_page_type(lnpo.page) == 2) &&
		    (nand_phy_low_page_cache_get_for_read(
			 &lnpo, nsci->two_plane) == 0))
			return 0;
	} else {
		;
	}

	ret |= generic_rw_page(&lnpo, function, nsci->two_plane);

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_erase_super_block(struct _nand_physic_op_par *npo)
{
	int ret;

	ret = generic_use_chip_function(npo, 0);

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_read_super_page(struct _nand_physic_op_par *npo)
{
	int ret;

	ret = generic_rw_use_chip_function(npo, 0);

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_write_super_page(struct _nand_physic_op_par *npo)
{
	int ret;

	ret = generic_rw_use_chip_function(npo, 1);

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_super_bad_block_check(struct _nand_physic_op_par *npo)
{

	int ret;

	ret = generic_use_chip_function(npo, 1);

	return ret;
}

/*****************************************************************************
 *Name         :
 *Description  :
 *Parameter    :
 *Return       : 0:ok  -1:fail
 *Note         :
 *****************************************************************************/
int generic_super_bad_block_mark(struct _nand_physic_op_par *npo)
{
	int ret;

	ret = generic_use_chip_function(npo, 2);

	return ret;
}

struct df_read_page_end df_read_page_end = {
    .read_page_end = generic_read_page_end_not_retry,
};

struct rawnand_ops rawnand_ops = {
    .erase_single_block = generic_erase_block,
    .write_single_page = generic_write_page,
    .read_single_page = generic_read_page,
    .single_bad_block_check = generic_bad_block_check,
    .single_bad_block_mark = generic_bad_block_mark,
    .erase_super_block = generic_erase_super_block,
    .write_super_page = generic_write_super_page,
    .read_super_page = generic_read_super_page,
    .super_bad_block_check = generic_super_bad_block_check,
    .super_bad_block_mark = generic_super_bad_block_mark,
};
